import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/cupertino.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:lib_dns/manager/ntt_dns_manager.dart';
import 'package:oktoast/oktoast.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:xf_evaluate/constants/vad_constants.dart';
import 'package:xf_evaluate/manager/entity/xf_eval_entity.dart';
import 'package:xf_evaluate/manager/entity/xf_socket_result_entity.dart';
import 'package:xf_evaluate/utils/toast_utils.dart';
import 'package:xf_evaluate/utils/xf_auth_utils.dart';
import 'package:xf_evaluate/xf_evaluate.dart';

import 'entity/xf_eval_result_entity.dart';

class XFEvalManager {
  static XFEvalManager? _instance;

  factory XFEvalManager._() {
    _instance ??= XFEvalManager._internal();
    return _instance!;
  }

  static XFEvalManager get instance => _instance ??= XFEvalManager._();

  XFEvalManager._internal() {
    _xfEvaluate = XfEvaluate();
    _xfEvaluate?.setChannelListener(onVadListener);
  }

  String _TAG = "[XFEvalManager]";

  /// vad
  XfEvaluate? _xfEvaluate;

  /// 录音
  FlutterSoundRecorder recorder = FlutterSoundRecorder();

  /// 播放器
  FlutterSoundPlayer player = FlutterSoundPlayer();

  /// 录音数据
  StreamSubscription? _mRecordingDataSubscription;

  /// 录音分贝以及进度
  // StreamSubscription? _dbPeakSubscription;

  /// 录音文件路径
  String _mPath = '';

  /// wav 录音文件路径
  String _wavPath = '';

  /// 采样率
  final int tSampleRate = 16000;

  /// socket客户端
  WebSocketChannel? _socketChannel;

  /// 回调
  Function(XFEvalResultEntity entity)? _onResult;

  /// 评测原文
  String _originalText = '';

  /// 是否取消评测 （就是不触发完成回调）
  bool _isCancel = false;

  /// 播放录音回调
  Function(PlaybackDisposition playback)? _onPlayback;

  StreamSubscription? _playListener;

  /// 评测结果
  String result = '';
  String resultBase64 = '';

  bool _isConvertWav = false;

  String? ip;

  /// 开始评测
  void startEval(XFEvalEntity entity) {
    _isCancel = false;
    stopRecord(
      isCancel: false,
      sendResult: false,
    );
    _playListener?.cancel();
    _connect(entity);
  }

  /// 连接WebSocket
  void _connect(XFEvalEntity entity) async {
    if (!await Permission.microphone.request().isGranted) {
      showToast('没有录音权限!');
      return;
    }
    try {
      if (_socketChannel == null) {
        if (!await canAccessDomain()) {
          ip = await NttDnsManager.instance.getHost(host: "ise-api.xfyun.cn");
        }
        var url = XfAuthUtils.getAuthUrl(ip: ip);
        debugPrint('debug 讯飞评测请求时链接…… $url');
        final wsUrl = Uri.parse(url);
        debugPrint('讯飞评测请求时链接 wsUrl …… ${wsUrl.toString()}');
        // IOWebSocketChannel.connect(url)
        // _socketChannel = IOWebSocketChannel.connect(wsUrl,headers: {
        //   'Host': ip
        // });
        // _socketChannel = WebSocketChannel.connect(wsUrl);
        _socketChannel = IOWebSocketChannel.connect(url,
            headers: {
              'Host': 'ise-api.xfyun.cn',
              'Origin': 'http://ise-api.xfyun.cn',
            },
            connectTimeout: const Duration(seconds: 20));
        await _socketChannel!.ready;
        _isConvertWav = false;
        _socketChannel!.stream.listen((message) {
          debugPrint('返回数据:$message');
          if (message != null) {
            XfSocketResultEntity entity =
                XfSocketResultEntity.fromJson(jsonDecode(message));
            if (entity.code != 0) {
              _onResult?.call(XFEvalResultEntity(
                path: _mPath,
                result: "",
                type: XFEvalEventType.error,
                originText: _originalText,
                errorCode: entity.code,
              ));
              stopRecord();
              _socketChannel!.sink.close();
              _socketChannel = null;
            }
            if (entity.data != null && entity.data!.status == 2) {
              /// 当返回最终结果数据时关闭连接
              _socketChannel!.sink.close();
              _socketChannel = null;
              if (!_isCancel) {
                if (entity.data!.data != null) {
                  var base64decode = base64Decode(entity.data!.data!);
                  var decode = utf8.decode(base64decode);
                  debugPrint('抛出评测结束 $_onResult');
                  result = decode;
                  resultBase64 = entity.data!.data!;

                  Timer.periodic(const Duration(milliseconds: 100), (timer) {
                    debugPrint('正在等待pcm转换为wav $_isConvertWav');
                    if (_isConvertWav) {
                      timer.cancel();
                      _isConvertWav = false;

                      /// 将数据抛出给业务处理
                      _onResult?.call(XFEvalResultEntity(
                          path: _wavPath,
                          result: decode,
                          type: XFEvalEventType.evalResult,
                          originText: _originalText,
                          resultBase64: entity.data!.data!));
                    }
                  });
                }
              }
            }
          }
        }, onError: (error) async {
          debugPrint('socket 连接出错！');
          ip = await NttDnsManager.instance.getHost(host: "ise-api.xfyun.cn");
          _onResult?.call(XFEvalResultEntity(
              path: _mPath,
              result: "",
              type: XFEvalEventType.error,
              originText: _originalText));
          stopRecord();
          _socketChannel!.sink.close();
          _socketChannel = null;
          resetVad();
        }, onDone: () {
          debugPrint('socket 连接关闭');
          resetVad();
          stopRecord();
        });
      }
      _originalText = entity.txt;

      _socketChannel!.sink.add(_getFirstData(entity));
      _openRecord();
    } catch (err) {
      debugPrint('WebSocketChannel err:$err');
      var code = 1001;
      if (err is TimeoutException) {
        // ToastUtils.show('当前网络不佳，请保持网络通畅后重试');
        code = 1002;
      }
      ip = await NttDnsManager.instance.getHost(host: "ise-api.xfyun.cn");
      _socketChannel?.sink.close();
      _socketChannel = null;
      _onResult?.call(XFEvalResultEntity(
          path: "",
          result: "",
          type: XFEvalEventType.error,
          originText: _originalText,
          errorCode: code));
    }
  }

  /// 判断当前域名是否能解析成功
  Future<bool> canAccessDomain() async {
    try {
      if (ip != null && ip!.isNotEmpty) {
        return false;
      }
      final result = await InternetAddress.lookup("ise-api.xfyun.cn")
          .timeout(const Duration(seconds: 5));
      if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
        return true; // 域名解析成功
      }
    } catch (e) {
      print('$_TAG 域名解析失败！！！ "ise-api.xfyun.cn" , Error: $e'); // 域名解析失败
    }

    return false; // 域名解析失败
  }

  /// 开启录音 在真正录音前需要先开启录音
  void _openRecord() async {
    /// 开启录音前先重置一下vad状态
    resetVad();

    await recorder.openRecorder();

    startRecord();
  }

  /// 播放录音
  void playRecord({String url = '', Codec codec = Codec.pcm16}) {
    player.openPlayer().then((value) {
      player.setSubscriptionDuration(const Duration(milliseconds: 100));
      _playListener = player.onProgress?.listen((event) {
        debugPrint('播放进度 ${event.position} e -> ${event.duration}');
        _onPlayback?.call(event);
      });
      player.startPlayer(
          fromURI: url.isEmpty ? _wavPath : url,
          codec: codec,
          whenFinished: () {
            debugPrint('播放完成');
            _onPlayback?.call(PlaybackDisposition(
                position: const Duration(seconds: 1),
                duration: const Duration(seconds: 1)));
          });
    });
  }

  /// 设置播放回调
  void setOnPlayback(Function(PlaybackDisposition playback) onPlayback) {
    _onPlayback = onPlayback;
  }

  /// 停止播放
  Future<void> stopPlayer() async {
    await player.stopPlayer();
  }

  /// 开始录音
  void startRecord() async {
    var sink = await createFile();
    _onResult?.call(XFEvalResultEntity(
        path: _mPath,
        result: "",
        type: XFEvalEventType.startRecord,
        originText: _originalText));
    var recordingDataController = StreamController<Food>();
    int aus = 0;
    _mRecordingDataSubscription =
        recordingDataController.stream.listen((buffer) {
      if (buffer is FoodData) {
        sink.add(buffer.data!);
        // 将音频数据分割为固定大小的帧
        int frameSize = 1280; // 每帧音频数据大小为 1280 字节
        int offset = 0;
        while (offset < buffer.data!.length) {
          int end = offset + frameSize;
          if (end > buffer.data!.length) {
            end = buffer.data!.length;
          }
          Uint8List frame = buffer.data!.sublist(offset, end);
          // debugPrint('实时流：$frame');
          String audioBase64String = base64Encode(Uint8List.fromList(frame));

          /// 向vad发送数据流
          _xfEvaluate?.detectVad(frame, frame.length);

          /// 第一帧 aus 需要传 1 ，中间帧传 2
          if (aus == 0) {
            aus = 1;
          } else {
            aus = 2;
          }

          /// 发送实时流数据到讯飞
          _socketChannel?.sink.add(_getAudioData(
              audioBase64String: audioBase64String, status: 1, aus: aus));

          offset += frameSize;
        }
      }
    });

    /// 必须要调用这个 否则 onProgress 不回调
    // recorder.setSubscriptionDuration(const Duration(milliseconds: 100));

    // _dbPeakSubscription = recorder.onProgress?.listen((event) {
    //   debugPrint('onProgress : ${event.duration}, event.decibels -> ${event.decibels}');
    // });

    _xfEvaluate?.asyncVadConfig(
        VadConstants.VAD_SEND_TIMEOUT, VadConstants.VAD_RECV_TIMEOUT);

    /// 添加vad监听
    _xfEvaluate?.setVadListener();

    /// 开始录音
    await recorder.startRecorder(
      toStream: recordingDataController.sink,
      codec: Codec.pcm16,
      sampleRate: tSampleRate,
      bitRate: 12800,
    );
  }

  /// 创建录音文件
  Future<IOSink> createFile() async {
    // var tempDir = await getTemporaryDirectory();
    var tempDir = await getTemporaryDirectory();
    _mPath = '${tempDir.path}/eval_audio.pcm';
    _wavPath = '${tempDir.path}/eval_audio.wav';
    var outputFile = File(_mPath);
    if (outputFile.existsSync()) {
      await outputFile.delete();
    }
    debugPrint('_mPath ==> $_mPath');
    return outputFile.openWrite();
  }

  ///停止录音
  /// [isCancel] 是否取消评测
  Future<void> stopRecord(
      {bool isCancel = false, bool sendResult = true}) async {
    debugPrint('debugPrint---> 调用了停止录音 ');
    await recorder.stopRecorder();

    if (sendResult) {
      /// 抛出停止录音回调
      _onResult?.call(XFEvalResultEntity(
          path: _mPath,
          result: "",
          type: XFEvalEventType.endRecord,
          originText: _originalText));
    }

    if (_mRecordingDataSubscription != null) {
      await _mRecordingDataSubscription!.cancel();
      _mRecordingDataSubscription = null;
      pcmToWav();
    }

    _isCancel = isCancel;

    // if(_dbPeakSubscription != null){
    //   _dbPeakSubscription!.cancel();
    //   _dbPeakSubscription = null;
    // }

    _xfEvaluate?.destroyVadListener();

    debugPrint('停止录音 路径: $_mPath');
    endEval();
    // channel?.sink.close();
    // channel = null;
  }

  /// pcm转wav
  void pcmToWav() async {
    try {
      if (!File(_mPath).existsSync()) {
        debugPrint('文件不存在');
        return;
      }
      var file = File(_wavPath);
      if (!file.existsSync()) {
        file.create(recursive: true);
      }
      convertPcmToWav(
          File(_mPath).readAsBytesSync(), _wavPath, tSampleRate, 1, 2);
      // final wav = await Wav.readFile(_mPath);
      // for (final chan in wav.channels) {
      //   for (int i = 0; i < chan.length; ++i) {
      //     chan[i] *= 2; // Decrease the volume.
      //   }
      // }

      // await wav.writeFile(_wavPath);
      // FlutterSoundHelper()
      //     .pcmToWave(
      //         inputFile: _mPath, outputFile: _wavPath, sampleRate: tSampleRate)
      //     .then((value) {
      //   _isConvertWav = true;
      // });
    } catch (e) {
      _isConvertWav = true;
      debugPrint('pcm转wav失败 $e');
    }
  }

  Uint8List amplifyPcm(Uint8List pcmData, double multiplier) {
    ByteData byteData = ByteData.sublistView(pcmData);
    for (int i = 0; i < pcmData.length; i += 2) {
      int sample = byteData.getInt16(i, Endian.little);
      sample = (sample * multiplier)
          .clamp(-32768, 32767)
          .toInt(); // Clamp to avoid overflow
      byteData.setInt16(i, sample, Endian.little);
    }
    return pcmData;
  }

  Future<void> convertPcmToWav(Uint8List pcmData, String outputFilePath,
      int sampleRate, int channels, double volumeMultiplier) async {
    int byteRate = sampleRate * channels * 2; // 2 bytes per sample (16-bit PCM)
    int blockAlign = channels * 2; // 2 bytes per sample

    Uint8List amplifiedPcmData = amplifyPcm(pcmData, volumeMultiplier);

    // WAV header
    List<int> header = [
      // RIFF header
      0x52,
      0x49,
      0x46,
      0x46,
      // 'RIFF'
      ..._intToBytes(amplifiedPcmData.length + 36, 4),
      // File size - 8 bytes for 'RIFF' and size itself
      0x57,
      0x41,
      0x56,
      0x45,
      // 'WAVE'

      // fmt subchunk
      0x66,
      0x6d,
      0x74,
      0x20,
      // 'fmt '
      0x10,
      0x00,
      0x00,
      0x00,
      // Subchunk1 size (16 for PCM)
      0x01,
      0x00,
      // Audio format (1 for PCM)
      ..._intToBytes(channels, 2),
      // Number of channels
      ..._intToBytes(sampleRate, 4),
      // Sample rate
      ..._intToBytes(byteRate, 4),
      // Byte rate
      ..._intToBytes(blockAlign, 2),
      // Block align
      0x10,
      0x00,
      // Bits per sample (16 bits)

      // data subchunk
      0x64,
      0x61,
      0x74,
      0x61,
      // 'data'
      ..._intToBytes(amplifiedPcmData.length, 4),
      // Subchunk2 size (number of bytes of PCM data)
    ];

    // Combine header and PCM data
    Uint8List wavData = Uint8List.fromList(header + amplifiedPcmData);

    // Write the WAV file
    final outputFile = File(outputFilePath);
    await outputFile.writeAsBytes(wavData);
    _isConvertWav = true;
  }

  // Helper function to convert integer to bytes (little endian)
  List<int> _intToBytes(int value, int length) {
    List<int> result = [];
    for (int i = 0; i < length; i++) {
      result.add(value & 0xFF);
      value = value >> 8;
    }
    return result;
  }

  /// 释放
  void dispose() {
    stopRecord();
    recorder.closeRecorder();
    player.closePlayer();
    _socketChannel?.sink.close();
    _socketChannel = null;
    _playListener?.cancel();
    _playListener = null;
    _onPlayback = null;
    resetVad();
  }

  /// 结束评测
  void endEval() {
    _socketChannel?.sink
        .add(_getAudioData(audioBase64String: "", status: 2, aus: 4));
    // channel?.sink.close();
  }

  //第一次发送的数据
  String _getFirstData(
    XFEvalEntity entity,
  ) {
    String ent = convertType(entity);
    String category = convertCategory(entity);
    String content = (ent == "en_vip" && category == "read_word")
        ? "[word]\n${entity.txt}"
        : entity.txt;
    // 构建 JSON 数据
    Map<String, dynamic> data = {
      "common": {
        "app_id": XfAuthUtils.appId,
      },
      "business": {
        "aue": "raw",
        "auf": "audio/L16;rate=16000",
        "category": category,
        "cmd": "ssb",

        /// 打分宽松度
        "check_type": "easy",
        "ent": ent,
        "sub": "ise",
        "text": "\uFEFF$content",
        "ttp_skip": true,
        "rstcd": "utf8",
        "tte": "utf-8",
        "rst": "entirety",
        "ise_unite": '1',
        "extra_ability": "syll_phone_err_msg;pitch;multi_dimension",
      },
      "data": {"status": 0, "data": ""}
    };
    // 转换为 JSON 字符串
    String firstDataString = jsonEncode(data);
    debugPrint('第一次发送的数据：$firstDataString');
    return firstDataString;
  }

  /// 获取语音类型
  String convertType(XFEvalEntity entity) {
    switch (entity.languageType) {
      case XFEvalLanguageType.en:

        /// 英文
        return 'en_vip';
      case XFEvalLanguageType.zh:

        /// 中文
        return 'cn_vip';
      default:
        return 'en_vip';
    }
  }

  /// 获取评测类型 句子、单词、等等
  String convertCategory(XFEvalEntity entity) {
    switch (entity.contentType) {
      case XFEvalContentType.EN_SENTENCE:
        var startLetterIndex = -1;
        var noLetterIndex = -1;
        for (var index = 0; index < entity.txt.characters.length; index++) {
          var char = entity.txt.characters.elementAt(index);
          if (startLetterIndex == -1 && char.isLetter()) {
            debugPrint('评测类型1');
            startLetterIndex = index;
          } else if (startLetterIndex != -1 && !char.isLetter()) {
            noLetterIndex = index;
            debugPrint('评测类型2');
          } else if (char.isLetter() &&
              startLetterIndex != -1 &&
              noLetterIndex != -1) {
            debugPrint('评测类型3');

            ///除了头部和尾部，中间有其它字符包括空格，可认为是一个句子
            return 'read_sentence';
          }
        }
        debugPrint('评测类型4');

        /// 除了头部和尾部，中间没有其它字符，连空格都没有，那就认定是一个单词
        return "read_word";
      case XFEvalContentType.ZH_CHS_SENTENCE:

        /// 中文句子
        var startLetterIndex = -1;
        for (var index = 0; index < entity.txt.characters.length; index++) {
          var c = entity.txt.characters.elementAt(index);
          if (startLetterIndex == -1 && c.isChineseCharacter()) {
            startLetterIndex = index;
          } else if (c.isChineseCharacter() || startLetterIndex != -1) {
            ///有两个以上的中文字符
            return "read_sentence";
          }
        }

        /// 只有一个中文字符
        return "read_word";
      case XFEvalContentType.PHONETICIZE:

        /// 拼音 暂时不支持
        return "read_sentence";
      case XFEvalContentType.READ_CHAPTER:
        return "read_chapter";
      case XFEvalContentType.EN_WORD:
        return "read_word";
      case XFEvalContentType.READ_SYLLABLE:
        return 'read_syllable';
      default:
        return 'read_sentence';
    }
  }

  //后续音频参数数据
  String _getAudioData({
    required String audioBase64String,
    int status = 1,
    int aus = 1,
  }) {
    Map<String, dynamic> data = {
      "business": {
        "cmd": "auw",
        "aue": "raw",
        "aus": aus,
      },
      "data": {
        "status": status,
        "data": audioBase64String,
        "data_type": 1,
        "encoding": "raw",
      }
    };
    String audioDataString = jsonEncode(data);
    // debugPrint('后续音频参数数据:$audioDataString');
    return audioDataString;
  }

  /// 设置评测结果监听
  void setEvalListener(Function(XFEvalResultEntity entity) onResult) {
    debugPrint('设置监听1111 $onResult');
    _onResult = onResult;
  }

  /// 设置语音质量监听
  onVadListener(String method, dynamic callback) {
    debugPrint('$method , callback $callback');
    if (method == "vadCallback") {
      if (callback is Map && callback.containsKey('eventType')) {
        if (callback["eventType"] == VadConstants.VAD_SEND_TIMEOUT) {
          /// 前置超时
          _onResult?.call(XFEvalResultEntity(
              path: _mPath, result: "", type: XFEvalEventType.timeout));
          stopRecord(sendResult: false);
        } else if (callback["eventType"] == VadConstants.VAD_RECV_TIMEOUT) {
          /// 后置超时
          stopRecord();
        }
      }
    }
  }

  /// 重置vad
  void resetVad() {
    debugPrint('重置vad');
    _xfEvaluate?.destroyVadListener();
    _xfEvaluate?.resetVad();
  }
}

extension StringExtension on String {
  /// 判断是否为中文
  bool isChinese() {
    return RegExp(r'[\u4e00-\u9fa5]').hasMatch(this);
  }

  /// 判断是否为英文
  bool isEnglish() {
    return RegExp(r'[a-zA-Z]').hasMatch(this);
  }

  /// 判断单个字符是否为中文
  bool isChineseCharacter() {
    int codeUnit = codeUnitAt(0);
    return (codeUnit >= 0x4e00 && codeUnit <= 0x9fa5);
  }

  /// 判断字符是否为字母
  bool isLetter() {
    int codeUnit = codeUnitAt(0);
    return ((codeUnit >= 0x0041 && codeUnit <= 0x005A) ||
        (codeUnit >= 0x0061 && codeUnit <= 0x007A));
  }
}
